package com.wkbb.sports.utils;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.wkbb.sports.dto.*;

import com.wkbb.sports.enums.SportsRuleTypeEnum;
import com.wkbb.sports.model.HlifeOrgGrade;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class MatchRule {
	
	@Data
	public class Entity {
		private double score;
		private double plusScore = 0;
	}
	
	private List<ScoreRuleDto> rules;
	private Map<Long, Long> gradeMapping = new HashMap<>();;
	
	public MatchRule(List<ScoreRuleDto> rules, List<HlifeOrgGrade> orgGrades) {
		if(rules == null || rules.size() <= 0) {
			throw new RuntimeException("rules is empty!");
		}
		if(orgGrades == null || orgGrades.size() <= 0) {
			throw new RuntimeException("orgGrade mapping is empty!");
		}
		this.rules = rules;
		for(HlifeOrgGrade orgGrade : orgGrades) {
			gradeMapping.put(orgGrade.getGradeId(), orgGrade.getRuleGradeId());
		}
	}
	
	
	public Entity calculatePhysicalScore(Long gradeId, Byte sex, Double ach)throws Exception {
		
		log.info("calculatePhysicalScore(gradeId:{}, sex:{}, ach:{})", gradeId, sex, ach);
		
		Entity student = new Entity();
		
		if(gradeId == null) {
			throw new RuntimeException("gradeId is empty!");
		}
		
		if(sex == null) {
			throw new RuntimeException("sex is empty!");
		}
		
		if(ach == null) {
			throw new RuntimeException("achievement is empty!");
		}
		
		Long ruleGradeId = gradeMapping.get(gradeId);
		
		if(ruleGradeId == null) {
			throw new RuntimeException("not find ruleGradeId  for gradeId:" + gradeId);
		}
		
		ScoreRuleDto scoreRule = matchScoreRule(ruleGradeId);
		if(scoreRule == null) {
			throw new RuntimeException("not match any score rule for (gradeId:" + gradeId + ")");
		}
		List<ScoreRuleItemDto> items = scoreRule.getItems();
		if(items == null) {
			throw new RuntimeException("not match any rule items for (gradeId:" + gradeId + ")");
		}
		
		SportsRuleTypeEnum type = scoreRule.getRuleType().intValue() == SportsRuleTypeEnum.ASC.getValue()? SportsRuleTypeEnum.ASC : SportsRuleTypeEnum.DESC;

		ScoreRuleItemDto item = matchRuleItem(items, sex, type, ach);
		if(item == null) {
			log.info("not match any rule item");
		}else {
			student.score = item.getScore();
		}
		

		List<ScoreRulePlusDto> pluses = scoreRule.getPluses();
		ScoreRulePlusDto plus = matchRulePlus(pluses, sex, type, ach);
		if(plus == null) {
			log.info("not match any rule plus");
		}else {
			student.plusScore = plus.getScore();
		}
		
		
		return student;
	}
	
	private ScoreRuleDto matchScoreRule(long gradeId) {
		if(rules == null) {
			return null;
		}
		for(ScoreRuleDto rule : rules) {
			if(compareValue(rule.getGradeId(), gradeId)) {
				log.info(" matched(grade:{})", rule.getGradeId());
				return rule;
			}
		}
		
		return null;
	}
	
	private ScoreRuleItemDto matchRuleItem(List<ScoreRuleItemDto> items, byte sex, SportsRuleTypeEnum ruleType, double achievement) {
		if(items == null) {
			return null;
		}
		for(ScoreRuleItemDto item : items) {
			if((item.getSex() == 0 || compareValue(item.getSex(), sex))) {
				if(matchScope("item:" + item.getId(), item.getMin(), item.getMax(), ruleType, achievement)) {
					return item;
				}
			}
			
		}
		return null;
	}
	
	private ScoreRulePlusDto matchRulePlus(List<ScoreRulePlusDto> pluses, byte sex, SportsRuleTypeEnum ruleType, double achievement) {
		if(pluses == null) {
			return null;
		}
		for(ScoreRulePlusDto plus : pluses) {
			if((plus.getSex() == 0 || compareValue(plus.getSex(), sex))) {
				if(matchScope("plus:" + plus.getId(), plus.getMin(), plus.getMax(), ruleType, achievement)) {
					return plus;
				}
			}
		}
		return null;
	}

	private boolean matchScope(String prefix, double min, double max, SportsRuleTypeEnum ruleType, double value) {
		boolean reval;
		String msg;
		if(ruleType == SportsRuleTypeEnum.DESC) {
			reval = value >= min && value < max;
			msg = " matched(" + prefix + ", " + min + " <= " + value + " < " + max + ")";
		}else {
			reval = value >= min && value < max;
			msg = " matched(" + prefix + ", " + min + " <= " + value + " < " + max + ")";
		}
		if(reval) {
			log.info(msg);
		}
		return reval;
	}
    
    private boolean compareValue(Number n1, Number n2) {
    	if(n1 == null || n2 == null) {
    		return false;
    	}
    	if(n1 instanceof Long) {
    		return n1.longValue() == n2.longValue();
    	}
    	if(n1 instanceof Byte) {
    		return n1.byteValue() == n2.byteValue();
    	}
    	return n1.intValue() == n2.intValue();
    }
}
